<template>
  <div class="knowledge-graph-container" :style="{ paddingTop: navBarHeight + 'px' }">
    <div ref="graphContainer" class="knowledge-graph">
      <div class="search-bar">
        <input
            v-model="searchTerm"
            @keyup.enter="filterGraph"
            placeholder="输入节点名称进行过滤"
            class="search-input"
        >
<!--        <select v-model="selectedLinkType" class="link-type-select">-->
<!--          <option value="">选择关系类型</option>-->
<!--          <option v-for="type in linkTypes" :key="type" :value="type">{{ type }}</option>-->
<!--        </select>-->
        <button @click="filterGraph">搜索</button>
        <button @click="resetGraph">重置</button>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted, watch, nextTick } from 'vue'
import * as d3 from 'd3'

const props = defineProps({
  nodes: { type: Array, required: true, default: () => [] },
  links: { type: Array, required: true, default: () => [] },
  stats: {
    type: Object,
    required: true,
    default: () => ({ nodeCount: 0, edgeCount: 0 })
  },
  navHeight: {
    type: Number,
    default: 60
  }
})

const graphContainer = ref(null)
let simulation = null
const searchTerm = ref('')
const selectedLinkType = ref('')
const originalNodes = ref([...props.nodes])
const originalLinks = ref([...props.links])
const filteredNodes = ref([])
const filteredLinks = ref([])
const navBarHeight = ref(props.navHeight)

// 定义关系类型
const linkTypes = [
  'HAS_FEATURE',
  'HAS_PARAMETER',
  'USED_IN',
  'MADE_OF',
  'PRODUCED_BY',
  'MAINTAINED_BY'
]

const filterGraph = () => {
  let nodesToShow = [...props.nodes]
  let linksToShow = [...props.links]

  // 节点名称过滤
  if (searchTerm.value) {
    const targetNode = props.nodes.find(node =>
        node.name.toLowerCase() === searchTerm.value.toLowerCase()
    )
    if (!targetNode) {
      console.log('Node not found')
      return
    }
    const relatedLinks = props.links.filter(link =>
        link.source.id === targetNode.id || link.target.id === targetNode.id
    )
    const relatedNodeIds = new Set([
      targetNode.id,
      ...relatedLinks.map(link => link.source.id),
      ...relatedLinks.map(link => link.target.id)
    ])
    nodesToShow = props.nodes.filter(node => relatedNodeIds.has(node.id))
    linksToShow = relatedLinks
  }

  // 关系类型过滤
  if (selectedLinkType.value) {
    linksToShow = linksToShow.filter(link => link.type === selectedLinkType.value)
    const relatedNodeIds = new Set([
      ...linksToShow.map(link => link.source.id),
      ...linksToShow.map(link => link.target.id)
    ])
    nodesToShow = nodesToShow.filter(node => relatedNodeIds.has(node.id))
  }

  // 如果没有搜索词和关系类型，则显示所有节点和关系
  if (!searchTerm.value && !selectedLinkType.value) {
    nodesToShow = [...props.nodes]
    linksToShow = [...props.links]
  }

  filteredNodes.value = nodesToShow
  filteredLinks.value = linksToShow
  updateGraph(nodesToShow, linksToShow)
}

const resetGraph = () => {
  searchTerm.value = ''
  selectedLinkType.value = ''
  updateGraph(props.nodes, props.links)
}

const updateGraph = (nodesToShow, linksToShow) => {
  if (!graphContainer.value) return

  if (simulation) {
    simulation.stop()
    d3.select(graphContainer.value).select('svg').remove()
  }

  const width = graphContainer.value.clientWidth || 800
  const height = graphContainer.value.clientHeight || 600

  const svg = d3.select(graphContainer.value)
      .append('svg')
      .attr('width', width)
      .attr('height', height)
      .style('border', '1px solid #ddd')

  const g = svg.append('g')

  const zoom = d3.zoom()
      .scaleExtent([0.1, 4])
      .on('zoom', (event) => g.attr('transform', event.transform))
  svg.call(zoom)

  simulation = d3.forceSimulation([...nodesToShow])
      .force('link', d3.forceLink([...linksToShow]).id(d => d.id).distance(100))
      .force('charge', d3.forceManyBody().strength(-300))
      .force('center', d3.forceCenter(width / 2, height / 2))
      .force('collision', d3.forceCollide().radius(30))

  const link = g.append('g')
      .selectAll('line')
      .data(linksToShow)
      .join('line')
      .attr('stroke', '#999')
      .attr('stroke-opacity', 0.6)
      .attr('stroke-width', 1)

  const node = g.append('g')
      .selectAll('circle')
      .data(nodesToShow)
      .join('circle')
      .attr('r', 8)
      .attr('fill', d => getNodeColor(d))
      .attr('stroke', '#fff')
      .attr('stroke-width', 2)
      .call(drag(simulation))
      .on('mouseover', handleMouseOver)
      .on('mouseout', handleMouseOut)

  const label = g.append('g')
      .selectAll('text')
      .data(nodesToShow)
      .join('text')
      .text(d => d.name)
      .attr('font-size', '12px')
      .attr('dx', 12)
      .attr('dy', 4)
      .style('pointer-events', 'none')

  // 添加用于显示描述的临时标签组
  const linkLabels = g.append('g')
      .attr('class', 'link-labels')

  function handleMouseOver(event, d) {
    // 找到与当前节点相关的边
    const relatedLinks = linksToShow.filter(link =>
        link.source.id === d.id || link.target.id === d.id
    )

    // 添加描述标签
    linkLabels.selectAll('text').remove()
    linkLabels.selectAll('text')
        .data(relatedLinks)
        .join('text')
        .text(link => link.description)
        .attr('x', link => (link.source.x + link.target.x) / 2)
        .attr('y', link => (link.source.y + link.target.y) / 2)
        .attr('font-size', '10px')
        .attr('fill', '#666')
        .attr('text-anchor', 'middle')
        .style('pointer-events', 'none')
  }

  function handleMouseOut() {
    // 移除所有描述标签
    linkLabels.selectAll('text').remove()
  }

  simulation.on('tick', () => {
    link
        .attr('x1', d => d.source.x)
        .attr('y1', d => d.source.y)
        .attr('x2', d => d.target.x)
        .attr('y2', d => d.target.y)

    node
        .attr('cx', d => d.x)
        .attr('cy', d => d.y)

    label
        .attr('x', d => d.x)
        .attr('y', d => d.y)
  })
}

const getNodeColor = (node) => {
  const colors = {
    'Technology': '#4CAF50',
    'Feature': '#2196F3',
    'Concept': '#9C27B0',
    'default': '#FF9800'
  }
  return colors[node.type] || colors.default
}

const drag = (simulation) => {
  const dragstarted = (event) => {
    if (!event.active) simulation.alphaTarget(0.3).restart()
    event.subject.fx = event.subject.x
    event.subject.fy = event.subject.y
  }

  const dragged = (event) => {
    event.subject.fx = event.x
    event.subject.fy = event.y
  }

  const dragended = (event) => {
    if (!event.active) simulation.alphaTarget(0)
    event.subject.fx = null
    event.subject.fy = null
  }

  return d3.drag()
      .on('start', dragstarted)
      .on('drag', dragged)
      .on('end', dragended)
}

watch([() => props.nodes, () => props.links], ([newNodes, newLinks]) => {
  originalNodes.value = [...newNodes]
  originalLinks.value = [...newLinks]
  updateGraph(newNodes, newLinks)
}, { immediate: true })

onMounted(async () => {
  await nextTick()
  updateGraph(props.nodes, props.links)
  window.addEventListener('resize', () => updateGraph(
      searchTerm.value || selectedLinkType.value ? filteredNodes.value : props.nodes,
      searchTerm.value || selectedLinkType.value ? filteredLinks.value : props.links
  ))
})

onUnmounted(() => {
  if (simulation) simulation.stop()
  window.removeEventListener('resize', updateGraph)
})
</script>

<style scoped>
.knowledge-graph-container {
  width: 100%;
  height: 100%;
  position: relative;
  min-height: 500px;
}

.knowledge-graph {
  width: 100%;
  height: 100%;
  background-color: white;
  border-radius: 4px;
  overflow: hidden;
  position: relative;
}

.search-bar {
  position: absolute;
  top: 10px;
  left: 10px;
  right: 10px;
  z-index: 10;
  padding: 10px;
  background-color: rgba(245, 245, 245, 0.9);
  display: flex;
  gap: 10px;
  align-items: center;
  border-radius: 4px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.search-input {
  flex: 1;
  padding: 8px;
  border: 1px solid #ddd;
  border-radius: 4px;
  font-size: 14px;
}

.link-type-select {
  padding: 8px;
  border: 1px solid #ddd;
  border-radius: 4px;
  font-size: 14px;
}

button {
  padding: 8px 16px;
  background-color: #409EFF;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 14px;
}

button:hover {
  background-color: #66b1ff;
}
</style>